﻿using Leo.Common.DataAnnotations;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Leo.Common
{
    [DebuggerDisplay("Name={Name};Value={Value};StringValue={StringValue};DisplayText={DisplayText}")]
    public class EnumDescriptor
    {
        public string Name { get; set; }

        public int Value { get; set; }

        public string StringValue { get; set; }

        public Type EnumType { get; set; }

        public string DisplayText
        {
            get
            {
                string result;
                if (this.Descriptions.TryGetValue("default", out result))
                {
                    return result;
                }
                return null;
            }
            set
            {
                this.Descriptions["default"] = value;
            }
        }

        internal const string DEFAULT = "default";

        private static ConcurrentDictionary<Type, List<EnumDescriptor>> cache = new ConcurrentDictionary<Type, List<EnumDescriptor>>();

        public Dictionary<string, string> Descriptions { get; internal set; } = new Dictionary<string, string>();

        public List<Attribute> Features { get; } = new List<Attribute>();

        public string GetDescription(string scene)
        {
            return this.GetDescription(scene, false);
        }

        public string GetDescription(string scene, bool defaultWhenNull)
        {
            if (string.IsNullOrEmpty(scene))
            {
                scene = "default";
            }
            string result = defaultWhenNull ? this.DisplayText : null;
            string result2;
            if (!this.Descriptions.TryGetValue(scene, out result2))
            {
                return result;
            }
            return result2;
        }

        internal static bool Match(EnumDescriptor ed, string s, bool ignoreCases = false)
        {
            StringComparison stringComparison = ignoreCases ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
            if (string.IsNullOrEmpty(s))
            {
                return false;
            }
            s = s.Trim();
            return s.Equals(ed.Name, stringComparison) || s.Equals(ed.StringValue, stringComparison) || s.Equals(ed.DisplayText, stringComparison);
        }

        public static EnumDescriptor GetDescriptor(Type enumType, object value)
        {
            if (value == null)
            {
                return null;
            }
            Type type = value.GetType();
            List<EnumDescriptor> descriptors = EnumDescriptor.GetDescriptors(enumType);
            if (type == typeof(string))
            {
                return Enumerable.FirstOrDefault<EnumDescriptor>(descriptors, (EnumDescriptor x) => EnumDescriptor.Match(x, value.ToString(), true));
            }
            return EnumDescriptor.GetDescriptor(enumType, Convert.ToInt32(value));
        }

        private static EnumDescriptor GetDescriptor(Type enumType, int enumVal)
        {
            List<EnumDescriptor> descriptors = EnumDescriptor.GetDescriptors(enumType);
            EnumDescriptor enumDescriptor = descriptors.Find((EnumDescriptor d) => d.Value == enumVal);
            if (enumDescriptor == null && enumVal != 0)
            {
                IEnumerable<EnumDescriptor> enumerable = Enumerable.Where<EnumDescriptor>(descriptors, (EnumDescriptor x) => (x.Value & enumVal) == x.Value);
                if (enumerable != null || Enumerable.Any<EnumDescriptor>(enumerable))
                {
                    enumDescriptor = new EnumDescriptor();
                    enumDescriptor.Value = enumVal;
                    enumDescriptor.Name = enumerable.Join((EnumDescriptor x) => x.Name, ',');
                    enumDescriptor.DisplayText = Enumerable.Where<EnumDescriptor>(enumerable, (EnumDescriptor x) => !string.IsNullOrEmpty(x.DisplayText)).Join((EnumDescriptor x) => x.DisplayText, ',');
                    enumDescriptor.StringValue = Enumerable.Where<EnumDescriptor>(enumerable, (EnumDescriptor x) => !string.IsNullOrEmpty(x.StringValue)).Join((EnumDescriptor x) => x.StringValue, ',');
                    using (IEnumerator<string> enumerator = Enumerable.Distinct<string>(Enumerable.SelectMany<EnumDescriptor, string>(enumerable, (EnumDescriptor x) => x.Descriptions.Keys)).GetEnumerator())
                    {
                        while (enumerator.MoveNext())
                        {
                            string item = enumerator.Current;
                            enumDescriptor.Descriptions[item] = string.Join(",", Enumerable.Select<EnumDescriptor, string>(enumerable, delegate (EnumDescriptor x)
                            {
                                string result2;
                                if (!x.Descriptions.TryGetValue(item, out result2))
                                {
                                    return x.Name;
                                }
                                return result2;
                            }));
                        }
                    }
                }
            }
            EnumDescriptor result;
            if ((result = enumDescriptor) == null)
            {
                (result = new EnumDescriptor()).Value = enumVal;
            }
            return result;
        }

        public static List<EnumDescriptor> GetDescriptors(Type enumType)
        {
            if (Enumeration.IsAssignableFrom(enumType))
            {
                return EnumDescriptor.GetEnumerationDescriptors(enumType);
            }
            if (enumType.IsEnum())
            {
                return EnumDescriptor.GetEnumDescriptors(enumType);
            }
            throw new ArgumentException();
        }

        private static List<EnumDescriptor> GetEnumerationDescriptors(Type enumType)
        {
            return EnumDescriptor.cache.GetOrAdd(enumType, delegate (Type typeOfValue)
            {
                IEnumerable<Enumeration> all = Enumeration.GetAll(enumType);
                List<EnumDescriptor> list = new List<EnumDescriptor>();
                foreach (Enumeration enumeration in all)
                {
                    EnumDescriptor enumDescriptor = new EnumDescriptor();
                    enumDescriptor.EnumType = enumType;
                    enumDescriptor.Name = enumeration.Name;
                    enumDescriptor.Value = enumeration.Value;
                    enumDescriptor.StringValue = enumeration.Name;
                    enumDescriptor.Descriptions = (Dictionary<string, string>)enumeration.Descriptions.Clone<string, string>();
                    string displayText;
                    if (enumDescriptor.Descriptions.TryGetValue("default", out displayText))
                    {
                        enumDescriptor.DisplayText = displayText;
                    }
                    list.Add(enumDescriptor);
                }
                return list;
            });
        }

        private static List<EnumDescriptor> GetEnumDescriptors(Type enumType)
        {
            return EnumDescriptor.cache.GetOrAdd(enumType, delegate (Type typeOfValue)
            {
                Type type = typeOfValue;
                if (typeOfValue.IsGenericType && typeOfValue.GetGenericTypeDefinition() == typeof(Nullable))
                {
                    type = Nullable.GetUnderlyingType(typeOfValue);
                }
                FieldInfo[] fields = type.GetFields();
                List<EnumDescriptor> list = new List<EnumDescriptor>();
                foreach (object obj in Enum.GetValues(type))
                {
                    EnumDescriptor descriptor = new EnumDescriptor();
                    descriptor.EnumType = enumType;
                    descriptor.Name = Enum.GetName(type, obj);
                    descriptor.Value = Convert.ToInt32(obj);
                    IEnumerable<Attribute> customAttributes = CustomAttributeExtensions.GetCustomAttributes(Enumerable.FirstOrDefault<FieldInfo>(fields, (FieldInfo t) => t.Name == descriptor.Name));
                    descriptor.Features.AddRange(customAttributes);
                    descriptor.DisplayText = EnumDescriptor.GetDisplayText(customAttributes);
                    IEnumerable<FieldDescriptionAttribute> enumerable = Enumerable.OfType<FieldDescriptionAttribute>(customAttributes);
                    if (enumerable != null && Enumerable.Any<FieldDescriptionAttribute>(enumerable))
                    {
                        foreach (FieldDescriptionAttribute fieldDescriptionAttribute in enumerable)
                        {
                            descriptor.Descriptions[fieldDescriptionAttribute.Scene] = fieldDescriptionAttribute.Description;
                        }
                    }
                    IEnumerable<StringValueAttribute> enumerable2 = Enumerable.OfType<StringValueAttribute>(customAttributes);
                    StringValueAttribute stringValueAttribute = (enumerable2 != null) ? Enumerable.FirstOrDefault<StringValueAttribute>(enumerable2) : null;
                    if (stringValueAttribute != null)
                    {
                        descriptor.StringValue = stringValueAttribute.Value;
                    }
                    if (string.IsNullOrEmpty(descriptor.DisplayText))
                    {
                        descriptor.DisplayText = descriptor.Name;
                    }
                    list.Add(descriptor);
                }
                return list;
            });
        }

        private static string GetDisplayText(IEnumerable<Attribute> attrs)
        {
            DescriptionAttribute descriptionAttribute = Enumerable.FirstOrDefault<DescriptionAttribute>(Enumerable.OfType<DescriptionAttribute>(attrs));
            if (descriptionAttribute != null)
            {
                return descriptionAttribute.Description;
            }
            DisplayAttribute displayAttribute = Enumerable.FirstOrDefault<DisplayAttribute>(Enumerable.OfType<DisplayAttribute>(attrs));
            if (displayAttribute != null)
            {
                return displayAttribute.Name;
            }
            return null;
        }

        public static string GetTypeName(Type enumType)
        {
            if (enumType.IsGenericType && enumType.GetGenericTypeDefinition() == typeof(Nullable))
            {
                enumType = Nullable.GetUnderlyingType(enumType);
            }
            return enumType.Name;
        }

        public static T ParseTo<T>(string s, bool ignoreCase = false)
        {
            return (T)((object)EnumDescriptor.ParseTo(typeof(T), s, ignoreCase));
        }

        public static bool TryParseTo<T>(string s, bool ignoreCase, out T instance)
        {
            bool result;
            try
            {
                instance = (T)((object)EnumDescriptor.ParseTo(typeof(T), s, ignoreCase));
                result = true;
            }
            catch
            {
                instance = default(T);
                result = false;
            }
            return result;
        }

        public static bool TryParseTo(Type enumType, string s, bool ignoreCase, out object instance)
        {
            bool result;
            try
            {
                instance = EnumDescriptor.ParseTo(enumType, s, ignoreCase);
                result = true;
            }
            catch
            {
                instance = null;
                result = false;
            }
            return result;
        }

        public static object ParseTo(Type enumType, string s, bool ignoreCase = false)
        {
            if (typeof(Enumeration).IsAssignableFrom(enumType))
            {
                return Enumeration.Parse(s, enumType);
            }
            if (enumType.IsEnum())
            {
                return EnumDescriptor.ParseToEnum(enumType, s, ignoreCase);
            }
            throw new InvalidCastException();
        }

        // 将字符串解析为枚举类型的方法
        private static object ParseToEnum(Type enumType, string s, bool ignoreCase = false)
        {
            // 检查是否是可为空类型
            bool isNullable = enumType.IsGenericType && enumType.GetGenericTypeDefinition() == typeof(Nullable<>);

            // 如果字符串为空且不是可为空类型，抛出异常
            if (string.IsNullOrEmpty(s) && !isNullable)
            {
                throw new ArgumentNullException($"无法将空值转换为枚举 {EnumDescriptor.GetTypeName(enumType)}");
            }

            // 如果字符串为空且是可为空类型，返回null
            if (string.IsNullOrEmpty(s))
            {
                return null;
            }

            // 获取实际的枚举类型
            if (isNullable)
            {
                enumType = Nullable.GetUnderlyingType(enumType);
            }

            // 如果类型不是枚举，抛出异常
            if (!enumType.IsEnum)
            {
                throw new ArgumentException("要转换的类型必须是枚举");
            }

            // 检查字符串是否为整数
            bool isInteger = Regex.IsMatch(s, "^-?\\d+$");

            // 检查整数是否为已定义的枚举值
            bool isDefinedEnum = isInteger && (Enum.IsDefined(enumType, int.Parse(s)) || EnumDescriptor.EnumHasFlag(enumType));

            string text = s;
            if (!isInteger || (!isDefinedEnum && int.Parse(s) != 0))
            {
                List<EnumDescriptor> descriptors = EnumDescriptor.GetDescriptors(enumType);
                string[] parts = s.Split(',');

                int result = 0;
                foreach (string part in parts)
                {
                    List<EnumDescriptor> matches = descriptors.FindAll(i => EnumDescriptor.Match(i, part, ignoreCase));
                    EnumDescriptor descriptor = null;

                    if (matches.Count == 1)
                    {
                        descriptor = matches[0];
                    }
                    else
                    {
                        descriptor = matches.Find(h => h.Name == s);
                        if (descriptor == null)
                        {
                            descriptor = matches.Find(h => h.StringValue == s);
                        }
                    }

                    if (descriptor == null)
                    {
                        throw new Exception($"枚举转换失败，枚举类型：{enumType.Name}，值：{s}");
                    }

                    result += descriptor.Value;
                }
                text = result.ToString();
            }

            return Enum.Parse(enumType, text);
        }

        private static bool EnumHasFlag(Type enumType)
        {
            return CustomAttributeExtensions.GetCustomAttribute<FlagsAttribute>(enumType) != null;
        }

        public static string GetName(Type enumType, object value)
        {
            EnumDescriptor descriptor = EnumDescriptor.GetDescriptor(enumType, value);
            if (descriptor == null)
            {
                return null;
            }
            return descriptor.Name;
        }

    }

    public class EnumDescriptor<T> : EnumDescriptor
    {
        public T Instance
        {
            get
            {
                return (T)((object)Enum.Parse(typeof(T), base.Name));
            }
        }
    }

}
